﻿using ServerCommon.Architecture;
using ServerCommon.Log;
using ServerCommon.Utility;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Security.Policy;
using System.Text;
using System.Threading.Tasks;
using Pb;

using uid_t = System.Int64;
namespace ServerCommon.Net
{
    public class NetNodeManager
    {
        private static LogHelper log = LogHelper.GetLogger(typeof(NetNodeManager));

        private bool _inited;
        public bool Inited => _inited;

        protected Dictionary<Guid, NetNode> dictGuidNodes = new Dictionary<Guid, NetNode>();
        protected Dictionary<long, NetNode> dictIdNodes = new Dictionary<uid_t, NetNode>();

        private ConcurrentQueue<ConnectionEventArgs> CacheDataMessage { get; } = new ConcurrentQueue<ConnectionEventArgs>();

        /// <summary>
        /// 性能警告时间（10毫秒）
        /// </summary>
        public const int PERFORMACE_WARN_TIME = 1000000; //放宽到100ms减少性能日志

        public NetNodeManager()
        {
            _inited = false;
        }

        /// <summary>
        /// 将网络IO事件入队
        /// </summary>
        /// <param name="connectionEventArgs"></param>
        public void PushIoEvent(ConnectionEventArgs connectionEventArgs)
        {
            CacheDataMessage.Enqueue(connectionEventArgs);
        }

        public virtual void Init()
        {
            _inited = true;

            log.Info("net node manager initialize success");
        }

        public virtual void SetupNetwork()
        {
        }

        public virtual void StopNetwork()
        {
            foreach(var item in dictGuidNodes)
            {
                item.Value.Stop();
            }
            dictGuidNodes.Clear();
            dictIdNodes.Clear();
        }

        public virtual void DoNetMessages()
        {
            ConnectionEventArgs entArgs = null;
            lock(CacheDataMessage)
            {
                while (CacheDataMessage.Count > 0)
                {
                    if (!CacheDataMessage.TryDequeue(out entArgs) || entArgs == null)
                    {
                        log.Error("doNetMessage entArgs is null");
                        continue;
                    }

                    try
                    {
                        switch (entArgs.EventType)
                        {
                            case EnumConnectionEventType.Accept:
                                {
                                    OnAccept(entArgs);
                                    break;
                                }
                            case EnumConnectionEventType.Connect:
                                {
                                    OnConnect(entArgs);
                                    break;
                                }
                            case EnumConnectionEventType.Disconnect:
                                {
                                    OnDisconnect(entArgs);
                                    break;
                                }
                            case EnumConnectionEventType.Connect_Error:
                                {
                                    OnConnectError(entArgs);
                                    break;
                                }
                            case EnumConnectionEventType.Recv:
                                {
                                    OnRecv(entArgs);
                                    break;
                                }
                            case EnumConnectionEventType.Close:
                                {
                                    OnClose(entArgs);
                                    break;
                                }
                        }
                    }
                    catch (Exception ex)
                    {
                        log.Error("throwing an exception when process a net client protocol", ex);
                    }
                }
            }
        }

        public virtual void ShowPrompt()
        {
            foreach(var item in dictGuidNodes)
            {
                item.Value.ShowPrompt();
            }
        }

        public virtual string GetPromptStr()
        {
            var strBuilder = new StringBuilder();

            foreach (var item in dictGuidNodes)
            {
                strBuilder.Append("Sessions: ");
                item.Value.ToLog();
                strBuilder.Append("\n");
            }

            return strBuilder.ToString();
        }

        public void Add(NetNode netnode)
        {
            lock (dictGuidNodes)
            {
                Guid nodeGid = netnode.HashCode;
                if (!dictGuidNodes.ContainsKey(nodeGid))
                {
                    dictGuidNodes.Add(nodeGid, netnode);
                }
            }

            lock(dictIdNodes)
            {
                long id = netnode.NodeId;
                if(!dictIdNodes.ContainsKey(id))
                {
                    dictIdNodes.Add(id, netnode);
                }
            }
        }

        public void Del(NetNode netnode)
        {
            lock (dictGuidNodes)
            {
                Guid nodeGid = netnode.HashCode;
                dictGuidNodes.Remove(nodeGid);
            }

            lock(dictIdNodes)
            {
                long id = netnode.NodeId;
                dictIdNodes.Remove(id);
            }
        }

        public bool HasNode(Guid guid)
        {
            return dictGuidNodes.ContainsKey(guid);
        }

        public bool HasNodeById(int id)
        {
            return dictIdNodes.ContainsKey(id);
        }

        public Guid GetNodeGuidById(uid_t id)
        {
            dictIdNodes.TryGetValue(id, out var node);
            return node.HashCode;
        }

        public NetNode GetNode(Guid guid)
        {
            lock (dictGuidNodes)
            {
                NetNode node = null;
                dictGuidNodes.TryGetValue(guid, out node);
                return node;
            }
        }

        public T GetNode<T>(Guid guid) where T : NetNode, new()
        {
            return GetNode(guid) as T;
        }

        public NetNode GetNodeById(uid_t id)
        {
            NetNode node = null;
            lock (dictIdNodes)
            {
                if (!dictIdNodes.TryGetValue(id, out node))
                {
                    return null;
                }
            }
            return node;
        }

        public T GetNodeById<T>(uid_t id) where T : NetNode, new()
        {
            return GetNodeById(id) as T;
        }

        public int GetNodeCount()
        {
            return dictGuidNodes.Count;
        }

        protected void OnMessageHandler(NetNode netNode, NetSession session, byte[] data)
        {
            /*var node = netNode as NetClient;
            if (data.Length < 4)
            {
                log.Error($"OnMessageHandler, can't parse message length={data.Length}");
                node.OnProtocolError();
                return;
            }

            var protocolId = BitConverter.ToInt32(data, 0);

            //需要在netNode中处理的消息
            var parser = ProtocolList.GetParser(netNode.ServiceType, protocolId);
            if (parser == null)
            {
                log.ErrorFormat($"OnMessageHandler, can't find parse, protocolId={protocolId}");
                return;
            }

            var protocol = parser.ParseFrom(data, 4, data.Length - 4) as IProtocol;
            if (protocol == null)
            {
                log.Error($"OnMessageHandler, parse protocol failed, protocolId={protocolId}");
                return;
            }
            protocol.OnProcess(netNode, session.HashCode);
            protocol.OnProcess(node);*/
        }

        public virtual void OnConnect(ConnectionEventArgs e)
        {
            long remoteNodeId = e.Session._remoteNodeId;
            long localNodeId = e.Session._localNodeId;
            if(dictIdNodes.TryGetValue(remoteNodeId, out NetNode node))
            {
                var req = new S2SServerInitReq() { ClientNodeId = localNodeId, ServiceNodeId = remoteNodeId };
                node.SendProtocol(e.Session, req);
            }
        }

        public virtual void OnDisconnect(ConnectionEventArgs e)
        {
        }

        public virtual void OnConnectError(ConnectionEventArgs e)
        {
        }

        public virtual void OnAccept(ConnectionEventArgs e)
        {
        }

        public virtual void OnRecv(ConnectionEventArgs e)
        {
            LogHelper.MarkSerialNumber();
            var proto = e.RecvedProtocol;
            NetNode node = dictIdNodes[e.NodeId];
            if (proto != null)
            {
                var performBegin = DateTime.Now.Ticks;

                if (e.RecvedProtocol != null)
                {
                    proto.OnProcess(node, e.Session);
                }
                else
                {
                    log.Debug("onRecv Error ");
                }

                var performSpan = DateTime.Now.Ticks - performBegin;
                if (performSpan > PERFORMACE_WARN_TIME)
                {
                    long ms = (long)(performSpan / TimeSpan.TicksPerMillisecond);
                    log.WarnFormat("OnProcess, performance warn,  ProtocolId:{0} spanTime:{1}ms ", e.RecvedProtocol.ProtocolId, ms);
                }
            }
            else
            {
                log.Debug("onRecv Error proto is null");
            }
        }
        public virtual void OnClose(ConnectionEventArgs e)
        {
            OnDisconnect(e);
        }

        public virtual void OnProtocolError()
        {
        }

    }
}
